function Harvester (battle) {
	// Harvester is the level 1 enemy
	// It's slow, don't aggressively attack, just move around, and find crystal to harvest.
	// Its attack range is very short, don't deal much damage, and only attack when it get attacked by the penguin.
	
	// Misc info
	var HARVESTER_SIZE_W = 80; // Image size of the harvester
	var HARVESTER_SIZE_H = 80; // Image size of the harvester

	var HARVESTER_OFFSET_X = (MAP_TILE_SIZE - HARVESTER_SIZE_W) * 0.5; // Offset from the top-left corner of the image, to where the actual harvester image is
	var HARVESTER_OFFSET_Y = (MAP_TILE_SIZE - HARVESTER_SIZE_H) * 0.5; // Offset from the top-left corner of the image, to where the actual harvester image is
	
	var HARVESTER_SPEED = MAP_TILE_SIZE * 0.001; // The move speed of the harvester
	
	var HARVESTER_POSITION_CORRECT_OFFSET = 10; // We will round the harvester position into the square position if it's just differ this much from the square position
	
	// AI decision
	var HARVESTER_AI_WANDER = 0;            // Move around aimlessly
	var HARVESTER_AI_GO_TO_HARVEST = 1;     // Follow the A* path to a mineral block. After that, switch to mining animation
	var HARVESTER_AI_HARVEST = 2;           // Stand still and mine. If get attacked, switch to attack the attacker
	var HARVESTER_AI_ATTACK_TARGET = 3;     // Attack the penguin
	
	// Direction enum
	var DIRECTION_DOWN = 0;   // Down
	var DIRECTION_LEFT = 1;   // Left
	var DIRECTION_UP = 2;     // Up
	var DIRECTION_RIGHT = 3;  // Right
	
	
	// The harvester have 3 states. Moving, attacking and harvesting.
	var HARVESTER_STATE_MOVING = 0;
	var HARVESTER_STATE_ATTACKING = 1;
	var HARVESTER_STATE_HARVESTING = 2;
	
	// Animation count
	var HARVESTER_ANIMATION_COUNT_MOVING = 4;           // Number of frame in moving animation
	var HARVESTER_ANIMATION_COUNT_ATTACKING = 4;        // Number of frame in attacking animation
	var HARVESTER_ANIMATION_COUNT_HARVESTING = 4;       // Number of frame in harvesting animation
	
	
	var HARVESTER_MOVE_FRAME_LENGTH = 150;              // The duration of each frame in moving animation
	var HARVESTER_HARVESTING_FRAME_LENGTH = 150;        // The duration of each frame in harvesting animation

	// These are the MUST HAVE properties.
	this.m_x = 0;             // Position x
	this.m_y = 0;             // Position y
	this.m_direction = 0;     // Direction
	this.m_HP = 0;            // Hit point
	this.m_status = 0;        // State of the enemy, stun, freeze, or ....
	
	
	// Additional properties
	this.m_state = 0;         // The animation state (moving, or attacking...)
	
	var animationCount = 0;   // This is used to count the duration before switching to the new frame in the animation
	this.m_animationStep = 0; // This is the frame index in the animation
	
	// For AI:
	this.m_intendToDo = 0;             // What the harvester will do next
	var harvestTimeCount = 0;
	
	var HARVESTER_CHANGE_MIND_CHANCE = 0.1;        // The chance which the harvester will stop doing what it's doing, and decide to do something else.
	
	var HARVESTER_AI_WANDER_DECIDE_CHANCE = 0.3;   // All decision chance need to have total of 1
	var HARVESTER_AI_HARVEST_DECIDE_CHANCE = 0.6;
	var HARVESTER_AI_ATTACK_DECIDE_CHANCE = 0.1;
	
	var HARVESTER_AI_RETRY_COUNT = 5;              // Number of retry when search for a resource node
	var HARVESTER_AI_HARVESTING_TIME = 8000;       // Time the harvester will harvest one resource node
	
	// Path finding
	var pathMap = null;                // Path map from the battle object
	var pathGrid = null;               // The path grid contain a 2D array (1 and 0) to define movable square
	var pathFinder = null;             // A* path finder object
	var pathWayPoint = null;           // The result waypoint after the path finding
	var pathWayPointProgress = 0;      // Which node in the waypoint is the harvester in
	var pathWayPointLength = 0;        // Total number of node in the waypoint
	
	// Image
	var spriteImage = g_graphicEngine.LoadImage("Data/ActionPhase/Enemy/Harvester.png");
	
	// Init source position in the image
	var actionSource = new Array();
	for (var i=HARVESTER_STATE_MOVING; i<=HARVESTER_STATE_HARVESTING; i++) {
		actionSource[i] = new Array();
		for (var j=DIRECTION_DOWN; j<=DIRECTION_RIGHT; j++) {
			actionSource[i][j] = new Array();
		}
	}
	
	// Hard-code the source info for all animation
	// Moving
	for (var i=DIRECTION_DOWN; i<=DIRECTION_RIGHT; i++) {
		for (var j=0; j<=HARVESTER_ANIMATION_COUNT_MOVING; j++) {
			actionSource[HARVESTER_STATE_MOVING][i][j]  = new SourceRect (spriteImage, HARVESTER_SIZE_W * j, HARVESTER_SIZE_H * i, HARVESTER_SIZE_W, HARVESTER_SIZE_H);
		}
	}
	
	// Harvesting
	for (var i=DIRECTION_DOWN; i<=DIRECTION_RIGHT; i++) {
		for (var j=0; j<=HARVESTER_ANIMATION_COUNT_HARVESTING; j++) {
			actionSource[HARVESTER_STATE_HARVESTING][i][j]  = new SourceRect (spriteImage, HARVESTER_SIZE_W * j + HARVESTER_SIZE_W * 4, HARVESTER_SIZE_H * i, HARVESTER_SIZE_W, HARVESTER_SIZE_H);
		}
	}
	
	
	
	
	
	
	this.Init = function () {
		// Get the path map pointer from the battle object
		pathMap = battle.pathMap;
		
		// Init the path grid based on that path map to used in path finding
		pathGrid = new PF.Grid (MAP_SIZE_W, MAP_SIZE_H);
		for (var i=0; i<MAP_SIZE_W; i++) {
			for (var j=0; j<MAP_SIZE_H; j++) {
				if (pathMap[i][j] > GROUND_EMPTY)  
					pathGrid.setWalkableAt(i, j, false);
			}
		}
		
		// Init the A* path finder
		pathFinder = new PF.AStarFinder();
	}
	
	
	
	this.Update = function () {
		if (this.m_intendToDo == HARVESTER_AI_WANDER)
			this.m_state = HARVESTER_STATE_MOVING;
		else if (this.m_intendToDo == HARVESTER_AI_GO_TO_HARVEST)
			this.m_state = HARVESTER_STATE_MOVING;
		else if (this.m_intendToDo == HARVESTER_AI_HARVEST)
			this.m_state = HARVESTER_STATE_HARVESTING;
		
		
		
		this.UpdateAnimation();
			
		if (this.m_state == HARVESTER_STATE_MOVING) {
			this.Move();
		}
		else if (this.m_state == HARVESTER_STATE_HARVESTING) {
			harvestTimeCount += g_deltaTime;
			if (harvestTimeCount > HARVESTER_AI_HARVESTING_TIME) {
				this.DecideWhatToDo();
			}
		}
	}
	
	
	
	this.UpdateAnimation = function () {
		// Update the animation frame based on what it is doing
		// We increase animation count based on deltaTime, when it's reach the frame length
		// We reset it, and switch to next frame.
		
		if (this.m_state == HARVESTER_STATE_MOVING) {
			animationCount += g_deltaTime;
			if (animationCount >= HARVESTER_MOVE_FRAME_LENGTH) {
				animationCount -= HARVESTER_MOVE_FRAME_LENGTH;
				if (animationCount > HARVESTER_MOVE_FRAME_LENGTH) {
					animationCount = 0;
				}
				
				this.m_animationStep ++;
				if (this.m_animationStep >= HARVESTER_ANIMATION_COUNT_MOVING) {
					this.m_animationStep = 0;
				}
			}
		}
		else if (this.m_state == HARVESTER_STATE_HARVESTING) {
			animationCount += g_deltaTime;
			if (animationCount >= HARVESTER_HARVESTING_FRAME_LENGTH) {
				animationCount -= HARVESTER_HARVESTING_FRAME_LENGTH;
				if (animationCount > HARVESTER_HARVESTING_FRAME_LENGTH) {
					animationCount = 0;
				}
				
				this.m_animationStep ++;
				if (this.m_animationStep >= HARVESTER_ANIMATION_COUNT_HARVESTING) {
					this.m_animationStep = 0;
				}
			}
		}
	}
	
	
	this.Move = function () {
		// Get the approximately cell position of the harvester
		var cx = (this.m_x / MAP_TILE_SIZE) >> 0;
		var cy = (this.m_y / MAP_TILE_SIZE) >> 0;
		
		// Move it based on it purpose
		if (this.m_intendToDo == HARVESTER_AI_WANDER) {
			// If moving aimlessly, continue to move, if reach obstacle, stop, and think what to do again.
			if (this.m_direction == DIRECTION_DOWN) {
				this.m_y += HARVESTER_SPEED * g_deltaTime;
				if (this.m_y >= MAP_TILE_SIZE * (MAP_SIZE_H-1)) {
					this.m_y = MAP_TILE_SIZE * (MAP_SIZE_H-1);
					this.DecideWhatToDo();
				}
				else if (pathMap[cx][cy+1] > GROUND_EMPTY) {
					this.m_y = cy * MAP_TILE_SIZE;
					this.DecideWhatToDo();
				}
			}
			else if (this.m_direction == DIRECTION_LEFT) {
				this.m_x -= HARVESTER_SPEED * g_deltaTime;
				if (this.m_x <= 0) {
					this.m_x = 0;
					this.DecideWhatToDo();
				}
				else if (pathMap[cx][cy] > GROUND_EMPTY) {
					this.m_x = cx * MAP_TILE_SIZE + MAP_TILE_SIZE;
					this.DecideWhatToDo();
				}
			}
			else if (this.m_direction == DIRECTION_UP) {
				this.m_y -= HARVESTER_SPEED * g_deltaTime;
				if (this.m_y <= 0) {
					this.m_y = 0;
					this.DecideWhatToDo();
				}
				else if (pathMap[cx][cy] > GROUND_EMPTY) {
					this.m_y = cy * MAP_TILE_SIZE + MAP_TILE_SIZE;
					this.DecideWhatToDo();
				}
			}
			else if (this.m_direction == DIRECTION_RIGHT) {
				this.m_x += HARVESTER_SPEED * g_deltaTime;
				if (this.m_x >= MAP_TILE_SIZE * (MAP_SIZE_W-1)) {
					this.m_x = MAP_TILE_SIZE * (MAP_SIZE_W-1);
					this.DecideWhatToDo();
				}
				else if (pathMap[cx+1][cy] > GROUND_EMPTY) {
					this.m_x = cx * MAP_TILE_SIZE;
					this.DecideWhatToDo();
				}
			}
			
			// If we very near the cell position, have a change to stop and think on what to do
			// If not continue moving, we correct its position to the cell position
			if (Math.abs(this.m_x - cx * MAP_TILE_SIZE) < HARVESTER_POSITION_CORRECT_OFFSET
			&&  Math.abs(this.m_y - cy * MAP_TILE_SIZE) < HARVESTER_POSITION_CORRECT_OFFSET) {
				if (Math.random() < HARVESTER_CHANGE_MIND_CHANCE) {
					//this.m_x = cx * MAP_TILE_SIZE;
					//this.m_y = cy * MAP_TILE_SIZE;
					this.DecideWhatToDo();
				}
			}
		}
		else if (this.m_intendToDo == HARVESTER_AI_GO_TO_HARVEST) {
			if      (this.m_direction == DIRECTION_DOWN)  this.m_y += HARVESTER_SPEED * g_deltaTime;
			else if (this.m_direction == DIRECTION_LEFT)  this.m_x -= HARVESTER_SPEED * g_deltaTime;
			else if (this.m_direction == DIRECTION_UP) 	  this.m_y -= HARVESTER_SPEED * g_deltaTime;
			else if (this.m_direction == DIRECTION_RIGHT) this.m_x += HARVESTER_SPEED * g_deltaTime;
			
			if (Math.abs(this.m_x - cx * MAP_TILE_SIZE) < HARVESTER_POSITION_CORRECT_OFFSET
			&&  Math.abs(this.m_y - cy * MAP_TILE_SIZE) < HARVESTER_POSITION_CORRECT_OFFSET) {
				if (cx == pathWayPoint[pathWayPointProgress][0]
				&&  cy == pathWayPoint[pathWayPointProgress][1]) {
					pathWayPointProgress ++;
					
					if (pathWayPointProgress == pathWayPointLength) {
						this.m_intendToDo = HARVESTER_AI_HARVEST;
						harvestTimeCount = 0;
						
						if      (cx > 0 && pathMap[cx-1][cy] == GROUND_RESOURCE)               this.m_direction = DIRECTION_LEFT;
						else if (cx < MAP_SIZE_W - 1 && pathMap[cx+1][cy] == GROUND_RESOURCE)  this.m_direction = DIRECTION_RIGHT;
						else if (cy > 0 && pathMap[cx][cy-1] == GROUND_RESOURCE)               this.m_direction = DIRECTION_UP;
						else if (cy < MAP_SIZE_H - 1 && pathMap[cx][cy+1] == GROUND_RESOURCE)  this.m_direction = DIRECTION_DOWN;
						
						this.m_x = cx * MAP_TILE_SIZE;
						this.m_y = cy * MAP_TILE_SIZE;
					}
					else {
						var oldDirection = this.m_direction;
						var newX = pathWayPoint[pathWayPointProgress][0];
						var newY = pathWayPoint[pathWayPointProgress][1];
						
						if      (newX > cx) this.m_direction = DIRECTION_RIGHT;
						else if (newX < cx) this.m_direction = DIRECTION_LEFT;
						else if (newY > cy) this.m_direction = DIRECTION_DOWN;
						else if (newY < cy) this.m_direction = DIRECTION_UP;
						
						if (oldDirection != this.m_direction) {
							this.m_x = cx * MAP_TILE_SIZE;
							this.m_y = cy * MAP_TILE_SIZE;
						}
					}
				}
			}
		}
	}
	
	this.Stop = function () {
		
	}
	
	
	this.DecideWhatToDo = function () {
		var cx = (this.m_x / MAP_TILE_SIZE) >> 0;
		var cy = (this.m_y / MAP_TILE_SIZE) >> 0;
					
		// This is the core of the AI decision tree.
		// The harvester will pick a purpose first. Then assign command accordingly.
		var decision = Math.random();
		if (decision < HARVESTER_AI_WANDER_DECIDE_CHANCE) {
			// OK, wander around
			var oldDirection = this.m_direction;
			
			this.m_intendToDo = HARVESTER_AI_WANDER; //Don't know what to do yet, just wander around then...
			this.m_direction = (Math.random() * 4) >> 0; //  Pick a new direction
			
			if (oldDirection != this.m_direction) {
				this.m_x = cx * MAP_TILE_SIZE;
				this.m_y = cy * MAP_TILE_SIZE;
			}
		}
		else if (decision < HARVESTER_AI_WANDER_DECIDE_CHANCE + HARVESTER_AI_HARVEST_DECIDE_CHANCE) {
			// It decide to go harvesting
			// Find a random resource point on the map
			// Scan through all square to find a square which adjacent to a resource node
			var suitableSquareX = new Array();
			var suitableSquareY = new Array();
			for (var i=0; i<MAP_SIZE_W; i++) {
				for (var j=0; j<MAP_SIZE_H; j++) {
					if (pathMap[i][j] == GROUND_RESOURCE) {
						if (i > 0 && pathMap[i-1][j] == GROUND_EMPTY) {
							var add = true;
							for (var k=0; k<suitableSquareX.length; k++) {
								if (suitableSquareX[k] == i-1 && suitableSquareY[k] == j) {
									add = false;
								}
							}
							if (add) {
								suitableSquareX.push (i-1); suitableSquareY.push (j);
							}
						}
						if (i < MAP_SIZE_W - 1 && pathMap[i+1][j] == GROUND_EMPTY) {
							var add = true;
							for (var k=0; k<suitableSquareX.length; k++) {
								if (suitableSquareX[k] == i+1 && suitableSquareY[k] == j) {
									add = false;
								}
							}
							if (add) {
								suitableSquareX.push (i+1); suitableSquareY.push (j);
							}
						}
						if (j > 0 && pathMap[i][j-1] == GROUND_EMPTY) {
							var add = true;
							for (var k=0; k<suitableSquareX.length; k++) {
								if (suitableSquareX[k] == i && suitableSquareY[k] == j-1) {
									add = false;
								}
							}
							if (add) {
								suitableSquareX.push (i); suitableSquareY.push (j+1);
							}
						}
						if (j < MAP_SIZE_H - 1 && pathMap[i][j+1] == GROUND_EMPTY) {
							var add = true;
							for (var k=0; k<suitableSquareX.length; k++) {
								if (suitableSquareX[k] == i-1 && suitableSquareY[k] == j) {
									add = false;
								}
							}
							if (add) {
								suitableSquareX.push (i); suitableSquareY.push (j+1);
							}
						}
					}
				}
			}
			
			var retryCount = 0;
			pathWayPointLength = 0;
			while (pathWayPointLength == 0) {
				if (suitableSquareX.length == 0) {
					// If there is no resource node
					this.m_intendToDo = HARVESTER_AI_WANDER;
					this.m_direction = (Math.random() * 4) >> 0;
					return;
				}
				else {
					var pickIndex = (Math.random() * suitableSquareX.length) >> 0;
					FindPath (cx, cy, suitableSquareX[pickIndex], suitableSquareY[pickIndex]);
					if (pathWayPointLength == 0) {
						retryCount ++;
						suitableSquareX.splice (pickIndex, 1);
						suitableSquareY.splice (pickIndex, 1);
						
						if (retryCount > HARVESTER_AI_RETRY_COUNT) {
							// If can't pick a resource node, wander instead
							this.m_intendToDo = HARVESTER_AI_WANDER;
							this.m_direction = (Math.random() * 4) >> 0;
							return;
						}
					}
					else {
						this.m_intendToDo = HARVESTER_AI_GO_TO_HARVEST;
					}
				}
			}
		}
		else if (decision < HARVESTER_AI_WANDER_DECIDE_CHANCE + HARVESTER_AI_HARVEST_DECIDE_CHANCE + HARVESTER_AI_ATTACK_DECIDE_CHANCE) {
			
		}
	}
	
	

	this.Draw = function () {
		// Draw the harvester on screen
		g_graphicEngine.Draw (g_context, actionSource[this.m_state][this.m_direction][this.m_animationStep].m_id, actionSource[this.m_state][this.m_direction][this.m_animationStep].m_x, actionSource[this.m_state][this.m_direction][this.m_animationStep].m_y,
			actionSource[this.m_state][this.m_direction][this.m_animationStep].m_w, actionSource[this.m_state][this.m_direction][this.m_animationStep].m_h, this.m_x + HARVESTER_OFFSET_X + BATTLE_OFFSET_X, this.m_y + HARVESTER_OFFSET_Y + BATTLE_OFFSET_Y, HARVESTER_SIZE_W, HARVESTER_SIZE_H, 1);
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	// PATH FINDER -------------------------------------------------------------
	var FindPath = function (sx, sy, dx, dy) {
		pathWayPoint = pathFinder.findPath (sx, sy, dx, dy, pathGrid.clone());
		pathWayPointProgress = 0;
		pathWayPointLength = pathWayPoint.length;
	}
	// PATH FINDER -------------------------------------------------------------
}
